package parser;

import java.util.ArrayList;

import exception.ParseResidueException;

import residue.AbstractResidue;
import residue.GenericComposedResidue;
import residue.GenericMonosaccharideResidue;
import residue.GenericRepeatResidue;
import residue.GenericSubstituentResidue;
import residue.Link;
import residue.iupac.monosaccharide.*;
import residue.iupac.repeat.Rep;
import residue.iupac.substituent.*;
import residue.iupac.substituent.virtual.Alkylacylglycol;
import residue.iupac.substituent.virtual.Cer;
import residue.iupac.composed.*;
import sugar.*;
import sun.org.mozilla.javascript.ast.ThrowStatement;
import sun.security.action.GetLongAction;
import utils.TreeTools;

public class IupacResidue {

	

	private String sequence ="";
	private int rank;
	private String id=""; 
	private String branchId="";
	private IupacBranch branch = null;
	
	private AbstractResidue abstractResidue=null;
	
	private boolean isRepeat = false;
	
	private IupacRepeatTree repeat = null;
	private IupacUndCapTree undCap = null;
	

	/*Constructor */	
	protected IupacResidue( IupacBranch branch) //throws Exception
	{
		this.branch = branch;
//		this.branchId = branch.getId();
	}
	
	protected IupacResidue(String sequence, int rank, IupacBranch branch) throws Exception
	{
		this.sequence=sequence;
		this.rank=rank;		
		this.branch = branch;		
		this.id =buildResidueId();

		this.parse();
		
	}
	
//	protected IupacResidue(String sequence, int rank, IupacBranch branch, boolean isRepeat) throws Exception
//	{
//		this.sequence=sequence;
//		this.rank=rank;		
//		this.branch = branch;		
//		this.id =buildResidueId();
//		this.isRepeat=isRepeat;
//		
////		if(!isRepeat)
////		{
//			this.parse();
////		}
//		
//	}
	
	protected IupacResidue(String sequence, int rank, IupacBranch branch, IupacRepeatTree repeat) throws Exception
	{
		this.sequence=sequence;
		this.rank=rank;		
		this.branch = branch;		
		this.id =buildResidueId();
		this.repeat=repeat;
		
//		if(!isRepeat)
//		{
			this.parse();
//		}
		
	}
	
	/*Accessor */
	public AbstractResidue getAbstractResidue() {
		return abstractResidue;
	}

	public void setAbstractResidue(AbstractResidue abstractResidue) {
		this.abstractResidue = abstractResidue;
	}
	
	public int getRank() {
		return rank;
	}
	public void setRank(int rank) {
		this.rank = rank;
	}
	public String getSequence() {
		return sequence;
	}
	public void setSequence(String sequence) {
		this.sequence = sequence;
	}
	
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getBranchId() {
		return branchId;
	}

	public void setBranchId(String branchId) {
		this.branchId = branchId;
	}
	
	public IupacBranch getBranch() {
		return branch;
	}
	
	public void setBranch(IupacBranch branch) {
		this.branch = branch;
	}
	
	public boolean getIsRepeat() {
		return isRepeat;
	}

	public void setIsRepeat(boolean isRepeat) {
		this.isRepeat = isRepeat;
	}

	public IupacRepeatTree getRepeat() {
		return repeat;
	}

	

	public void setRepeat(IupacRepeatTree repeat) {
		this.repeat = repeat;
	}

	public IupacUndCapTree getUndCap() {
		return undCap;
	}
	
	public void setUndCap(IupacUndCapTree undCap) {
		this.undCap = undCap;
	}
	
	@Override
	public String toString() {
//		System.out.println("IupacResidue.toString() : " + super.toString() );
//		System.out.println("	Sequence : " + this.sequence );
//		System.out.println("	Rank : " + this.rank );
//		System.out.println("	Id : " + this.id );
//		System.out.println("	Branch : " + this.branch );
		return super.toString();
	}
	
	protected String buildResidueId() //
	{	
		String id="";
		
		id= this.branch.getId() + '.' +String.valueOf(this.rank);
//		System.out.println("IupacResidue buildResidueId for " +  " : "+ id);
		
		return id;
	}
	
	public void parse() throws Exception
	{
		if(isRepeat)//abstractResidue.isGenericRepeatResidue())//
		{
//			this.sequence = "Rep(a1-2)";
			System.out.println("IupacResidue parse() multitude " + this.repeat.getMultitudeMin() + " " + this.sequence);
//			multitude = TreeTools.getMultitude(this.sequence, closingCurlybracketPosition);
		
//			repeat.setSequence(this.sequence);
//			repeat.setMultitudeMin(multitudeMin)
		}
		else
		{		
			try
			{
				abstractResidue = parseResidue(this.sequence);
				
			}
			catch(ParseResidueException ex)
			{
				throw ex;//new Exception("IupacResidue parse(), error parsing abstractResidue : " + ex.getMessage());
//				System.err.println("IupacResidue parse(), error parsing abstractResidue : " + ex.getMessage());
			}
		
			try
			{
				Link link = parseLink();
				//link.setInferredLinkageTypes();
	
				if(abstractResidue.isGenericRepeatResidue())
				{
					((GenericRepeatResidue)abstractResidue).setLinkToPrevious(link);
	//				System.out.println("IupacResidue parse() isGenericRepeatResidue " );
				}
				if(abstractResidue.isGenericMonosaccharideResidue())
				{
					((GenericMonosaccharideResidue)abstractResidue).setLinkToPrevious(link);
				}
				if(abstractResidue.isGenericSubstituentResidue())
				{
					((GenericSubstituentResidue)abstractResidue).setLinkToPrevious(link);
				}
				if(abstractResidue.isGenericComposedResidue())
				{	
					GenericMonosaccharideResidue mon = ((GenericComposedResidue)abstractResidue).getMonosaccharide();
					mon.setLinkToPrevious(link);
				
					ArrayList<GenericSubstituentResidue> subs = ((GenericComposedResidue)abstractResidue).getSubstituents();
					
					for(int i=0;i<subs.size();i++)
					{
					Link l = subs.get(i).getLinkToPrevious();//new Link();
					
					if(l==null)
					{
						l = new Link();
					}
					
					l.setRes1(mon);
					l.setRes2(subs.get(i));					
					l.setRes1Id(this.id);
					l.setRes2Id(this.id);	
					l.setInferredLinkageTypes();

					subs.get(i).setLinkToPrevious(l);					
					}
				}
//				else
//				{
//					throw new Exception("IupacResidue parse() error");
//				}
				//linkToPrevious = parseLink();
			}
			
			catch(Exception ex)
			{
//				System.err.println("IupacResidue parse(), error parsing link : " + ex.getMessage());
				throw ex;
			}
			
		}
	}
	
	
	public boolean isRoot(String seq)
	{
		boolean res = true;
		
		if(seq.contains("("))
		{
			res = false;
		}
		return res;
	}
	
	
	public boolean isTreeRoot()
	{
		boolean test = this.id.equals(Constants.ROOT_RESIDUE_ID);
//		System.out.println("IupacResidue isTreeRoot() : " + this + " : " + test);
		return test;
	}
	
	
	
	public AbstractResidue parseResidue(String sequence) throws Exception
	{	
		/*P can interfere in the parsing of the link*/
		AbstractResidue res = null;//new AbstractResidue(); 
		String residueSeq = "";
		
		
		
		try
		{	
			if(!(isRoot(sequence)))
			{
//				System.out.println("parseResidue Sequence : " + sequence +  " isRoot : " + isRoot(sequence));
				int endSeq = sequence.lastIndexOf('(')  ;
				residueSeq = sequence.substring(0,endSeq );//this.getSequence().lastIndexOf("("));//"Xyl";//(0,3);//
//				System.out.println("IupacResidue parseResidue Residue (NOT root) : " + residueSeq +  " range : 0;" + (endSeq ));
			}
			else if(isRoot(sequence))
			{
				residueSeq = sequence;
//				System.out.println("IupacResidue parseResidue Residue (root) : " + residueSeq);
			}
			

			
			/*Compare uppercase abstractResidue*/
			//monosaccharide
			if(residueSeq.toUpperCase().equals("Alt".toUpperCase())) {res = new Alt();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Ara".toUpperCase())) {res = new Ara();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Fru".toUpperCase())) {res = new Fru();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Fuc".toUpperCase())) {res = new Fuc();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Gal".toUpperCase())) {res = new Gal();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Glc".toUpperCase())) {res = new Glc();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Man".toUpperCase())) {res = new Man();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Rha".toUpperCase())) {res = new Rha();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Sor".toUpperCase())) {res = new Sor();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Xyl".toUpperCase())) {res = new Xyl();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Kdn".toUpperCase())) {res = new Kdn();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Ido".toUpperCase())) {res = new Ido();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Tyv".toUpperCase())) {res = new Tyv();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Qui".toUpperCase())) {res = new Qui();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Bac".toUpperCase())) {res = new Bac();res.isCreated();}
			
			else if(residueSeq.toUpperCase().equals("GlcA".toUpperCase())) {res = new GlcA();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("GalA".toUpperCase())) {res = new GalA();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("ManA".toUpperCase())) {res = new ManA();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("IdoA".toUpperCase())) {res = new IdoA();res.isCreated();}
			
			else if(residueSeq.toUpperCase().equals("Galf".toUpperCase())) {res = new Galf();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("dAlt".toUpperCase())) {res = new dAlt();res.isCreated();}
			
			else if(residueSeq.toUpperCase().equals("Hex".toUpperCase())) {res = new Hex();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Pent".toUpperCase())) {res = new Pent();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("deoxyHex".toUpperCase())) {res = new DeoxyHex();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Gro".toUpperCase())) {res = new Gro();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("GroA2".toUpperCase())) {res = new GroA2();res.isCreated();}
			
			//substituent
			else if(residueSeq.toUpperCase().equals("Ac".toUpperCase())) {res = new Ac();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Acetyl".toUpperCase())) {res = new Ac();res.isCreated();}			
//			else if(residueSeq.toUpperCase().equals("H2po3".toUpperCase())) {res = new H2po3();res.isCreated();}
//			else if(residueSeq.toUpperCase().equals("Hso3".toUpperCase())) {res = new Hso3();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Me".toUpperCase())) {res = new Me();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("N".toUpperCase())) {res = new N();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("NAc".toUpperCase())) {res = new NAc();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("NGc".toUpperCase())) {res = new NGc();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("P".toUpperCase())) {res = new P();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("S".toUpperCase())) {res = new S();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("F".toUpperCase())) {res = new F();res.isCreated();}
			
			else if(residueSeq.toUpperCase().equals("Cer".toUpperCase())) {res = new Cer();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("alkylacylglycol".toUpperCase())) {res = new Alkylacylglycol();res.isCreated();}
			
			
	
			//composed
			else if(residueSeq.toUpperCase().equals("GalN".toUpperCase())) {res = new GalN();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("GalNAc".toUpperCase())) {res = new GalNAc();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("GlcN".toUpperCase())) {res = new GlcN();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("GlcNAc".toUpperCase())) {res = new GlcNAc();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("NeuAc".toUpperCase())) {res = new NeuAc();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("NeuGc".toUpperCase())) {res = new NeuGc();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("ManNAc".toUpperCase())) {res = new ManNAc();res.isCreated();}
			
			else if(residueSeq.toUpperCase().equals("HexNAc".toUpperCase())) {res = new HexNAc();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Neu?c".toUpperCase())) {res = new NeuAc();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("NeuAc2".toUpperCase())) {res = new NeuAc2();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Neu4,5Ac2".toUpperCase())) {res = new Neu45Ac2();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Neu5,9Ac2".toUpperCase())) {res = new Neu59Ac2();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Neu5,?Ac2".toUpperCase())) {res = new Neu5_Ac2();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("Neu5,?,?Ac3".toUpperCase())) {res = new Neu5__Ac3();res.isCreated();}
			else if(residueSeq.toUpperCase().equals("BacAc2".toUpperCase())) {res = new BacAc2();res.isCreated();}
			
			/*create a reapeat*/
			else if(residueSeq.toUpperCase().equals("Rep".toUpperCase())) {
				res = new Rep();res.isCreated();
				int repeatIdx=TreeTools.getTopTreeRepeat(this.getBranch().getTree()).getRepeats().size();
				((GenericRepeatResidue)res).setIndex(repeatIdx);}
			
			
			else
			{
////				res=null;
////				System.out.println("IupacResidue parseResidue Residue NOT found :" + residueSeq);
				throw new ParseResidueException("IupacResidue parseResidue Residue NOT found :" + residueSeq);
			}
		}
		catch(Exception ex)
		{
			res=null;
			ex.printStackTrace();
			System.err.println("IupacResidue parseResidue Residue sequence problem : " + residueSeq);
			throw new ParseResidueException("IupacResidue parseResidue Residue sequence problem : " + residueSeq);
		}
		if(res.equals(null))
		{
			throw new ParseResidueException("IupacResidue parseResidue residue is null!");
		}
		return res;
		
	}
	
	public String buildLinkSequence(String sequence) //throws Exception
	{
		String linkSeq = "";
		
		//if isRoot : do nothing
		if(!isRoot(sequence))//this.sequence))
		{
			try
			{	
				int startSeq = sequence.lastIndexOf(Constants.openingParenthesis) +1 ;
				int endSeq = sequence.lastIndexOf(Constants.closingParenthesis)  ;
				
				linkSeq = sequence.substring(startSeq,endSeq);//this.getSequence().lastIndexOf("("));//"Xyl";//(0,3);//
//				System.out.println("IupacResidue buildLinkSequence !isRoot seq :" + sequence);
//				System.out.println("IupacResidue buildLinkSequence !isRoot linkSeq :" + linkSeq);
			}
			catch(Exception ex)
			{
				System.err.println("IupacResidue buildLinkSequence !isRoot seq error : " + linkSeq +" " + ex.getMessage());
				//throw new Exception("IupacResidue buildLinkSequence !isRoot seq error : " + linkSeq +" " + ex.getMessage());
			}
		} 
		return linkSeq;
	}
	
	public Link parseLink() throws Exception
	{
		Link link = null;
		String linkSeq = buildLinkSequence(this.sequence);
		
		try
		{
//			System.out.println("IupacResidue parseLink link" );
			if(!this.isTreeRoot())
			{
//				link = createLink(linkSeq);
				link = new Link(linkSeq, this);
			}
			else
			{
//				System.out.println("IupacResidue parseLink residue : " + this.sequence);
				Link rootLink = new Link(linkSeq, this);				
				this.getBranch().getTree().setRootLink(rootLink);
				
				
				
				if(linkSeq==null || linkSeq.length()==0)
				{
					rootLink.setRes2Anomer(getAnomerFromGlycanType());
				}	
				setAnomerToResidue(rootLink.getRes2Anomer());									
			}
//			System.out.println("IupacResidue parseLink link : " + link.toString());
		}
		catch(Exception ex)
		{
			if(!this.isTreeRoot())
			{
				System.err.println("IupacResidue parseLink error : " + this.sequence + " / " + linkSeq +" " + ex.getMessage());
			}
			//throw ex;
		}
		return link;
	}
	
	public String getAnomerFromGlycanType()
	{
		String anomer = Constants.UNKNOWN;
		
		//if(this.id.equals("0.0"))
		if(isTreeRoot())
		{					
			//for root abstractResidue: N-LINKED (-> beta), O-LINKED (-> alpha)
			if(this.getBranch().getTree().getGlycanType().equals(Constants.O_LINKED))
			{anomer=Anomer.Alpha.getSymbol();}
			if(this.getBranch().getTree().getGlycanType().equals(Constants.N_LINKED))
			{anomer=Anomer.Beta.getSymbol();}
		}
//		System.out.println("IupacResidue getAnomerFromGlycanType  :" + this.getBranch().getTree().getGlycanType() +", anomer: " + anomer);
		
		return anomer;
	}
	
	public void setAnomerToResidue(String anomer)
	{
		Anomer a = Anomer.fromString(anomer.toString());
		
		GenericMonosaccharideResidue m = new GenericMonosaccharideResidue();
		GenericComposedResidue c = new GenericComposedResidue();
		try
		{
			//GenericMonosaccharideResidue
			if(this.abstractResidue.isGenericMonosaccharideResidue())
			{
				//((GenericMonosaccharideResidue)this.residue).setAnomer(a);
				(m.getClass().cast(this.abstractResidue)).setAnomer(a);
//				System.out.println("IupacResidue parseResidue parseLink setAnomerToResidue : " + (m.getClass().getName()));
//				System.out.println("IupacResidue parseResidue parseLink setAnomerToResidue : " + (m.getClass().cast(this.abstractResidue)).getAnomer());
			}
			//GenericComposedResidue
			else if(this.abstractResidue.isGenericComposedResidue())
			{
				(c.getClass().cast(this.abstractResidue)).getMonosaccharide().setAnomer(a);
//				System.out.println("IupacResidue parseResidue parseLink setAnomerToResidue : " + (c.getClass().getName()));
//				System.out.println("IupacResidue parseResidue parseLink setAnomerToResidue : " + (c.getClass().cast(this.abstractResidue)).getMonosaccharide().getAnomer());
			}
		}
		catch(Exception ex)
		{
			System.err.println("IupacResidue parseResidue setAnomerToResidue error : "+ anomer);
		}
	}
	
	public boolean isComposedResidue()
	{
		return this.abstractResidue.isGenericComposedResidue();	
	}
}
